package com.mazaiting.web.advice;

import com.mazaiting.common.core.domain.result.Result;
import com.mazaiting.common.core.domain.result.ResultCode;
import com.mazaiting.common.core.ex.exceptions.BaseException;
import com.mazaiting.common.core.ex.exceptions.BusinessException;
import com.mazaiting.common.core.ex.runtime.BaseRuntimeException;
import com.mazaiting.common.core.ex.runtime.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;

import java.util.Objects;

/**
 * 捕获异常
 *
 * @author mazaiting
 */
@Slf4j
@ResponseBody
@ControllerAdvice
public class GlobalExceptionAdvice {
    /**
     * 当controller中抛出Exception，则捕获
     *
     * @param e 异常
     * @return 结果
     */
    @ExceptionHandler(value = Exception.class)
    public Result<Object> exceptionHandler(Exception e) {
        e.printStackTrace();
        log.error("Exception: {}", e.getMessage());
        return Result.failed();
    }

    /**
     * 基础异常
     *
     * @param e 基础异常
     * @return 异常信息
     */
    @ExceptionHandler(value = BaseException.class)
    public Result<Object> exceptionHandler(BaseException e) {
        e.printStackTrace();
        log.error("BaseException: {}", e.getMessage());
        return Result.failed(e.getCode(), e.getMessage());
    }

    /**
     * 自定义异常
     *
     * @param e 基础异常
     * @return 异常信息
     */
    @ExceptionHandler(value = BusinessException.class)
    public Result<Object> exceptionHandler(BusinessException e) {
        e.printStackTrace();
        log.error("BusinessException: {}", e.getMessage());
        return Result.failed(e.getCode(), e.getMessage());
    }

//    /**
//     * 字符串类型注解校验异常
//     *
//     * @param e 基础异常
//     * @return 异常信息
//     */
//    @ExceptionHandler(value = ConstraintViolationException.class)
//    public Result<Object> exceptionHandler(ConstraintViolationException e) {
//        e.printStackTrace();
//        logger.debug(e.getMessage(), e);
//        return Result.failed(ResultCode.PARAM_IS_BLANK.getCode(),e.getMessage());
//    }


    /**
     * 自定义异常：参数类型不匹配
     *
     * @param e 基础异常
     * @return 异常信息
     */
    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    public Result<Object> exceptionHandler(MethodArgumentTypeMismatchException e) {
        e.printStackTrace();
        log.error("MethodArgumentTypeMismatchException: {}", e.getMessage());
        return Result.failed(ResultCode.PARAM_TYPE_ERROR);
    }

    /**
     * 参数校验异常
     *
     * @param e 参数校验异常
     */
    @ExceptionHandler(value = BindException.class)
    public Result<Object> exceptionHandler(BindException e) {
        log.error("BindException: {}", e.getMessage());
        FieldError fieldError = e.getBindingResult().getFieldError();
        if (Objects.nonNull(fieldError)) {
            String defaultMessage = fieldError.getDefaultMessage();
            log.error(defaultMessage, e);
            if (StringUtils.hasLength(defaultMessage)) {
                // 异常名称
                String validationExceptionName = ValidationException.class.getName() + ": ";
                // 判断是否包含
                int index = defaultMessage.lastIndexOf(validationExceptionName);
                // 取自定义异常的内容
                if (index > 0){
                    defaultMessage = defaultMessage.substring(index + validationExceptionName.length());
                }
            }
            return Result.failed(ResultCode.PARAM_CHECK_ERROR, defaultMessage);
        } else {
            return Result.failed(ResultCode.PARAM_CHECK_ERROR);
        }
    }

    /**
     * Controller上一层相关异常
     */
    @ExceptionHandler({
            NoHandlerFoundException.class,
            HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class,
            MissingPathVariableException.class,
            MissingServletRequestParameterException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
            HttpMessageNotWritableException.class,
            MethodArgumentNotValidException.class,
            HttpMediaTypeNotAcceptableException.class,
            ServletRequestBindingException.class,
            ConversionNotSupportedException.class,
            MissingServletRequestPartException.class,
            AsyncRequestTimeoutException.class
    })
    public Result<Object> handleServletException(Exception e) {
        e.printStackTrace();
        log.error("HandleServletException: {}", e.getMessage());
        //SERVLET_ERROR(-102, "servlet请求异常"),
        return Result.failed(ResultCode.SERVLET_ERROR.getMessage());
    }


    /**
     * 自定义异常：参数类型不匹配
     *
     * @param e 基础异常
     * @return 异常信息
     */
    @ExceptionHandler(value = Throwable.class)
    public Result<Object> exceptionHandler(Throwable e) {
        e.printStackTrace();
        log.error("Throwable: {}", e.getMessage());
        return Result.failed(ResultCode.SERVLET_ERROR);
    }

    /**
     * 参数校验异常
     *
     * @param e 参数校验异常
     */
    @ExceptionHandler(value = BaseRuntimeException.class)
    public Result<Object> exceptionHandler(BaseRuntimeException e) {
        log.error("BaseRuntimeException: {}", e.getMessage());
        return Result.failed(e.getCode(), e.getMessage());
    }

    /**
     * 主键冲突异常
     * @param e 主键冲突异常
     * @return 结果
     */
    @ExceptionHandler(value = DuplicateKeyException.class)
    public Result<Object> exceptionHandler(DuplicateKeyException e) {
        log.error("DuplicateKeyException: {}", e.getMessage());
        return Result.failed(ResultCode.DB_PRIMARY_KEY_CONFLICT);
    }

}
